﻿///////////////////////////////////////////////////////////////////////////////
// File:        EventLoop.cc
// Author:      671643387@qq.com
// Description:
///////////////////////////////////////////////////////////////////////////////

#include <cassert>
#include <boost/bind.hpp>
#include <nio/base/Thread.hpp>
#if defined(__WINDOWS__)
#include <nio/net/detail/SelectReactor.hpp>
#elif defined(__LINUX__)
#include <nio/net/detail/EpollReactor.hpp>
#endif

#include "nio/net/EventLoop.hpp"
using namespace nio::net;

#define __EL_MAX(a, b)  ((a) > (b)) ? (a) : (b)


EventLoop::EventLoop()
: state_(READY)
, cnfFrameTime_(16)
, frameUseTime_(0)
#if defined(__WINDOWS__)
, reactor_(new detail::SelectReactor())
#elif defined(__LINUX__)
, reactor_(new detail::EpollReactor())
#endif
, events_()
{

}

EventLoop::~EventLoop()
{

}

void EventLoop::queue(boost::function<void(void)> handler)
{
    assert(state_ == READY || state_ == RUNNING);
    struct _stEvent event = { base::TimeUtil::fromStartup(), handler };
    events_.push_back(event);
}

void EventLoop::run()
{
    assert(state_ == READY);
    setState(RUNNING);

    while (true)
    {
        base::time_t tmStart = base::TimeUtil::fromStartup();

        reactor_->doTick(__EL_MAX(1, cnfFrameTime_ - frameUseTime_));

        struct _stEvent event;
        Checker checker(this);
        while (events_.next(checker, event))
        {
            event.handler();
        }

        if (state_ == EXITING)
        {
            if (events_.empty())
                break;
            else
                continue;
        }

        frameUseTime_ = base::TimeUtil::fromStartup() - tmStart;
    }

    setState(EXIT);
}

void EventLoop::stop()
{
    assert(state_ == READY || state_ == RUNNING);
    queue(boost::bind(&EventLoop::doStop, this));
}